using System;
using System.Collections.Generic;
using System.Data;
using System.Text;

namespace Neo.MetaModel
{
    [Flags]
    public enum RelType
    {
        ToOne = 0x01,
        ToMany = 0x02,
        All = 0xFF
    }

    [Flags]
    public enum RelDirection
    {
        Parent = 0x01,
        Child = 0x02,
        Undefined = 0xFF
    }

    [Serializable]
    public class EntityRelationship
    {
        //--------------------------------------------------------------------------------------
        //	fields and constructor
        //--------------------------------------------------------------------------------------

        private Entity localEntity;
        private String varName;
        private String relationshipAttributes; /* custom attributes in the .NET sense */
        private String foreignTableName;
        private String localKey;
        private String foreignKey;
        private Rule updateRule;
        private Rule deleteRule;

        public EntityRelationship(Entity anEntity)
        {
            localEntity = anEntity;
        }


        //--------------------------------------------------------------------------------------
        //	accessors
        //--------------------------------------------------------------------------------------

        public Entity LocalEntity
        {
            get { return localEntity; }
        }

        public string VarName
        {
            set { varName = value; }
            get { return (varName != null) ? varName : ForeignEntity.ClassName; }
        }

        public String RelationshipAttributes
        {
            set { relationshipAttributes = value; }
            get { return relationshipAttributes; }
        }

        public String ForeignTableName
        {
            set { foreignTableName = value; }
            get { return foreignTableName; }
        }

        public string LocalKey
        {
            set { localKey = value; }
            get { return localKey; }
        }

        public string ForeignKey
        {
            set { foreignKey = value; }
            get { return foreignKey; }
        }

        public Rule UpdateRule
        {
            set { updateRule = value; }
            get { return updateRule; }
        }

        public Rule DeleteRule
        {
            set { deleteRule = value; }
            get { return deleteRule; }
        }


        //--------------------------------------------------------------------------------------
        //	derived properties
        //--------------------------------------------------------------------------------------

        public Entity ForeignEntity
        {
            get { return LocalEntity.Model.GetEntityForTable(foreignTableName); }
        }


        public RelType Type
        {
            get
            {
                if (ForeignEntity.ColumnIsPrimaryKey(ForeignKey))
                    return RelType.ToOne;
                return RelType.ToMany;
            }
        }

        public RelDirection Direction
        {
            get
            {
                if (ForeignEntity.ColumnIsPrimaryKey(ForeignKey))
                    return RelDirection.Child;
                if (LocalEntity.ColumnIsPrimaryKey(LocalKey))
                    return RelDirection.Parent;
                return RelDirection.Undefined;
            }
        }


        public EntityRelationship InverseRelationship
        {
            get
            {
                foreach (EntityRelationship r in ForeignEntity.Relationships)
                {
                    if ((r.ForeignEntity.TableName == LocalEntity.TableName) && (r.ForeignKey == LocalKey) && (r.LocalKey == ForeignKey))
                        return r;
                }
                return null;
            }
        }


        //--------------------------------------------------------------------------------------
        //	compatibility
        //--------------------------------------------------------------------------------------

        public string DotNetName
        {
            get { return VarName; }
        }

        public string DotNetType
        {
            get
            {
                EntityAttribute attribute = this.localEntity.GetAttributeForColumnName(this.localKey);
                if (attribute == null)
                {
                    return null;
                }
                return attribute.DotNetType;
            }
        }

        public string DotNetUpdateRule
        {
            get { return "Rule." + updateRule; }
        }


        public string DotNetDeleteRule
        {
            get { return "Rule." + deleteRule; }
        }

        public override string ToString()
        {
            if (String.IsNullOrEmpty(localEntity.ToString()))
            {
                return base.ToString();
            }
            return localEntity.ToString();
        }

        public string ToXml(Dictionary<string, int> dups)
        {
            StringBuilder sb = new StringBuilder();
            string relation = Direction == RelDirection.Child ? "foreign-key" : "iforeign-key";
            string foreignkey = "\t\t" + "<" + relation + @" foreignTable=""#foreigntable#"" name=""#name#"" onUpdate=""#onupdate#"" onDelete=""#ondelete#"">";
            string reference = "\t\t\t" + @"<#reference# local=""#local#"" foreign=""#foreign#""/>";
            //Must refer to tables name ! not the javaName
            foreignkey = foreignkey.Replace("#foreigntable#", ForeignTableName);
            string foreignTableName1 = EnsureUniqueName(ForeignEntity.ClassName, dups, Direction);
            foreignkey = foreignkey.Replace("#name#", foreignTableName1);
            foreignkey = foreignkey.Replace("#onupdate#", GetUpdateAndDeleteAction(UpdateRule));
            foreignkey = foreignkey.Replace("#ondelete#", GetUpdateAndDeleteAction(DeleteRule));
            reference = reference.Replace("#reference#", ((Direction == RelDirection.Child) ? "reference" : "ireference"));
            //Must refer to columns name ! not the javaName
            reference = reference.Replace("#local#", LocalKey);
            //Must refer to columns name ! not the javaName
            reference = reference.Replace("#foreign#", ForeignKey);
            sb.AppendLine(foreignkey);
            sb.AppendLine(reference);
            sb.AppendLine("\t\t</" + relation + ">");
            return sb.ToString();
        }

        private string EnsureUniqueName(string s, Dictionary<string, int> dups, RelDirection dir)
        {
            if (dir == RelDirection.Parent)
            {
                s = s + "Items";
            }
            else if (dir == RelDirection.Child)
            {
                //s = s + "Ref";
            }
            else
            {
                s = s + "Any";
            }

            if (dups.ContainsKey(s))
            {
                int cnt = dups[s];
                cnt++;
                dups[s] = cnt;
                return s + "_" + cnt;
            }
            else
            {
                dups[s] = 0;
                return s;
            }
        }

        private string GetUpdateAndDeleteAction(Rule rule)
        {
            if (rule == Rule.Cascade)
                return "cascade";
            if (rule == Rule.None)
                return "none";
            if (rule == Rule.SetDefault)
                return "setnull";
            if (rule == Rule.SetNull)
                return "none";
            return "none";
        }
    }
}